/*
 * PyVector.h
 *
 *  Created on: Dec 28, 2011
 *      Author: allan
 */

#ifndef PYVECTOR_H_
#define PYVECTOR_H_

// C++ includes
#include <vector>

// Boost includes
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
using namespace boost::python;

template<typename T>
struct std_vector_to_python_list
{
	static PyObject* convert(std::vector<T> const& v)
	{
		list l;
		
		BOOST_FOREACH(const T& val, v)
		{
			l.append(val);
		}

		return incref(l.ptr());
	}
};

template<typename T>
struct std_vector_from_python_list
{
	std_vector_from_python_list()
	{
		converter::registry::push_back(
			&convertible, &construct, type_id<std::vector<T>>());
	}

	static void* convertible(PyObject* obj_ptr)
	{
		if(!PyList_Check(obj_ptr))
			return 0;
		return obj_ptr;
	}

	static void construct(PyObject* obj_ptr, 
		converter::rvalue_from_python_stage1_data* data)
	{
		extract<list> x(obj_ptr);
		if(!x.check())
			throw_error_already_set();

		list l = x();

		void *storage = 
			((converter::rvalue_from_python_storage<std::vector<T>>*) data)->
				storage.bytes;
		
		new (storage) std::vector<T>();

		std::vector<T>& v = *reinterpret_cast<std::vector<T>*>(storage);

		for(int idx = 0; idx < len(l); ++idx)
		{
			extract<T> ext(l[idx]);
			if(!ext.check())
			{
				v.std::vector<T>::~vector<T>();
				throw_error_already_set();
			}

			v.push_back(ext());
		}

		data->convertible = storage;
	}
};

template<typename T>
void init_converter_std_vector_from_python_list()
{
	std_vector_from_python_list<T>();
}

// Reference: http://mail.python.org/pipermail/cplusplus-sig/2006-May/010346.html
namespace std
{
	template<typename T>
	std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec)
	{
		out << "[";
		
		for(std::size_t i = 0; i < vec.size(); ++i)
			out << vec[i] << ((i+1 != vec.size()) ? ", " : "");
		
		out << "]";
		
		return out; 
	}
}

template<typename T>
void export_std_vector(const char* type_name)
{
	init_converter_std_vector_from_python_list<T>();
	
	class_<std::vector<T>>(type_name)
		.def(vector_indexing_suite<std::vector<T>>());
}

template<typename T>
void export_std_vector_with_print(const char* type_name)
{
	init_converter_std_vector_from_python_list<T>();
	
	class_<std::vector<T>>(type_name)
		.def(vector_indexing_suite<std::vector<T>>())
		.def(self_ns::str(self_ns::self));
}

#endif /* PYVECTOR_H_ */
